#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main()
{
    //第一步：创建一个监听套接字
    int sock_listen = socket(AF_INET, SOCK_STREAM, 0);//第一个参数表示地址家族，基本上是AF_INET，表示使用因特网地址家族；第二个参数表示套接字类型，通常有两种，即SOCK_STREAM和SOCK_DGRAM，前者表示流式接套字，用于基于TCP协议的通信，后者表示数据报式套接字，用于基于UDP协议的通信；第三个通信表示使用的通信协议编号，0表示使用默认协议。
    //socket 函数的返回值为新套接字的
    
    if(sock_listen == -1)
    {
        perror("socket fail");
        return 1;
    }
    
    //第二步：绑定地址(IP + Port)
    struct sockaddr_in myaddr;//定义地址结构体
    myaddr.sin_family = AF_INET;//指定地址家族
    myaddr.sin_addr.s_addr = INADDR_ANY;//使用本机任意地址
    //myaddr.sin_addr.s_addr = inet_addr("114.116.17.31")//指定本机地址
    //inet_addr 函数作用：将字符串形式的IP地址转换成无符号32位整数形式（网络字节序）
    
    myaddr.sin_port = htons(5001);//指定使用的端口号为5001绑定地址建议使用1024以上的端口号，避免根系统应用、知名服务器应用冲突。另外，绑定1024以下的端口号
    //htons函数的作用：将某个短整数(short)从主机(host)字节转换为网络(network)字节序。
    
    if(-1 == bind(sock_listen, (struct sockaddr*)&myaddr, sizeof(myaddr)))
    {
        perror("bind");
        return 1;
    }
    
    //第三步：监听
    if(-1 == listen(sock_listen, 5))
    {
        perror("listen fail");
           
    }
    //listen函数的第二个参数表示连接等待队列的长度
    
    //第四步：接收客户端连接请求
    int sock_conn = accept(sock_listen, NULL, NULL);//accept函数参数：第一个为监听套字节描述符；第二个参数为地址结构体(struct sockaddr_in)指针，用于接受客户端地址信息，如果对客户端地址不感兴趣，就传NULL；第三个参数为地址结构体长度，用于接受客户端地址长度，如果对客户端地址不感兴趣，就传NULL； accept函数返回值为这条连接对应的套接字(通常称为连接套接字)，后面使用这个套接字进行收发数据。如果失败。其返回值为-1。调用accept函数时，如果没有任何客户端连接请求到来，该函数会阻塞调用线程直到成功接收到一个客户端连接请求或出现错误才返回
       if(sock_conn == -1)
       {
           perror("accept fail");
           return 1;
       }
    
    //第五步:收发数据
    char data1[100] = "你好，我是服务器";
    char data2[100];

    //发送给客户端
    int ret1 = send(sock_conn, data1, sizeof(data1), 0);//最后一个参数表示采用默认的方式发送数据,返回值为实际成功发送的字节数，小于或等于其第三个参数 和write函数等效
    if(ret1 == -1)
    {
        perror("send fail");
    }
    
    //接收客户端信息
    int ret2 = recv(sock_conn, data2, sizeof(data2), 0);  // 最后一个参数为 0 表示采用默认的方式接收数据，返回值>为实际成功接收的字节数，小于或等于其第三个参数
    //int ret = read(sock, data, sizeof(data));  // 和 recv 函数等效

    if(ret2 == -1)
    {
    	perror("recv fail");
    }
    else
    {
      	printf("客户端说：%s\n", data2);
    }

    //第六步：如果不需要继续和当前连接的客户端通信，就断开连接
    //close (sock_conn);
       
    //第七步：关闭监听套接字,如果不需要继续跟其他用户通信的话
    close(sock_listen);
}
